package com.mach.platform.config;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.mach.platform.security.*;
import com.mach.platform.utils.ReqStatus;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.io.PrintWriter;
import java.util.Map;

/**
 * @author Anonymous
 * @Description: spring security 安全配置内容
 * @Package: com.mach.platform.practice.config
 * @Time create on 2017/11/10 20:43
 */
@Slf4j
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserDetailServiceImpl userDetailsService;

	/**
	 * 放开某些文件或者请求接口不经过SpringSecurity安全限制，包括小程序的请求：/api/**
	 */
	private static final String[] PERMIT_APIS = new String[] { "/kaptcha.jpg", "/user/export","/vv", "/api/**"};

	@Override
	public void configure (WebSecurity web) {
		/**
		 * 1.配置swagger不经过SpringSecurity安全限制
		 */
		web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/configuration/ui",
				"/swagger-resources", "/configuration/security", "/swagger-resources/configuration/security",
				"/swagger-ui.html", "/webjars/**","/test/**","/go/**");

	}

	@Override
	protected void configure (HttpSecurity http) throws Exception {

		http.csrf().disable().authorizeRequests().antMatchers(PERMIT_APIS).permitAll().anyRequest()
		    .authenticated().and().formLogin().loginProcessingUrl("/login").failureHandler(new CuAntuFailureHandler())
		    .successHandler(new CuAntuSuccessHandler()).permitAll().and().logout().invalidateHttpSession(true)
		    .logoutSuccessHandler(new CuLogoutSuccessHandler()).permitAll();

		//检查验证码的过滤器，在验证账号密码之前的验证 暂时去掉
		//http.addFilterBefore(new KaptchaAuthenticationFilter("/login"), UsernamePasswordAuthenticationFilter.class);

		//只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
		//如果要前段后分离的，invalidSessionUrl 可配置为一个控制器中的请求，然后在该方法中返回json给前端.
/*		http.sessionManagement().maximumSessions(5).expiredSessionStrategy(new SessionInformationExpiredStrategy() {

			//替换ConcurrentSessionFilter 中 ResponseBodySessionInformationExpiredStrategy 中的 当被踢下后，返回给前端的信息
			//原来是返回一个  ：This session has been expired (possibly due to multiple concurrent ogins being attempted as the same user)
			//这里返回一个 json 让前端重新登录
			@Override
			public void onExpiredSessionDetected (SessionInformationExpiredEvent eventØ)
					throws IOException, ServletException {

				HttpServletResponse response = eventØ.getResponse();

				log.info("ResponseBodySessionInformationExpiredStrategy  并发重复登陆，踢下，要求再登录");
				response.setContentType("application/json;charset=utf-8");
				@Cleanup PrintWriter out = response.getWriter();
				Map map = Maps.newHashMap();
				map.put("status", ReqStatus.LOGOUT.getValue());
				map.put("msg", "该账户已有新的登录，此处已被踢下，需要重新登录吧！");
				out.write(JSON.toJSON(map).toString());
				response.flushBuffer();

			}
		});*/

		//没有验证的跳转界面
		http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {

            response.setContentType("application/json;charset=utf-8");
            @Cleanup PrintWriter out = response.getWriter();
            Map map = Maps.newHashMap();
            map.put("status", HttpStatus.FORBIDDEN.value());
            map.put("msg", accessDeniedException.getMessage());
            out.write(JSON.toJSON(map).toString());

        }).authenticationEntryPoint((request, response, authException) -> {

            log.info("没有登录进入该方法，如，项目重启，session没有，则进入，要求前端重新登录！");
            response.setContentType("application/json;charset=utf-8");
            @Cleanup PrintWriter out = response.getWriter();
            Map map = Maps.newHashMap();
            map.put("status", ReqStatus.LOGOUT.getValue());
            map.put("msg", "未拥有合法访问身份或合法身份过期，需要重新登录! ");
            map.put("data", "");
            out.write(JSON.toJSON(map).toString());

        });

	}

	@Override
	protected void configure (AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(daoAuthenticationProvider());
	}

	@Bean
	public AbstractUserDetailsAuthenticationProvider daoAuthenticationProvider ( ) throws Exception {
		CuAuthProvider provider = new CuAuthProvider();

		provider.setHideUserNotFoundExceptions(false);
		provider.setUserDetailsService(userDetailsService);
		provider.setPasswordEncoder(new BCryptPasswordEncoder());

		return provider;
	}

}
